補齊一些之前漏掉的關於 Function
的知識
在 Reason 綁定 function 和一般變數一樣
[@bs.val] external encodeURI: string => string = "encodeURI";
let result = encodeURI("hello");
可以利用標記參數來讓外部 Javascript
取得的函數更加易懂
[%%raw "function draw(x, y, border = false) {
return border ? (x + y): 0;
}"];
[@bs.val] external draw: (~x: int, ~y: int, ~border: bool=?, unit) => int = "";
Js.log(draw(~x=10, ~y=20, ~border=true, ())); /* 30 */
note: 這有一個特別的狀況,因為 border 是Option 的參數,所以最後需要有一個 unit
而在執行的時候的最後一個參數要傳入 ()
來避免警告
要使用物件中的 function
的時候有特別的方式來綁定 @bs.send
type processType;
let foo = () => Js.log("foo");
[@bs.send] external nextTick: (processType, (unit) => unit) => unit = "";
[@bs.val] external process: processType = "process";
nextTick(process, foo);
note: 使用 send 之後的函式第一個參數就是綁定的物件
function foo() {
console.log("foo");
return /* () */0;
}
process.nextTick(foo);
在 Javascript 中有的函數參數數量並沒有一定數量的
BuckleScript 支援這類的函式
但是前提是所有的類型都是要一樣的
遇到這種狀況就加上 bs.splice
在 external 的前面
[@bs.module "path"] [@bs.splice] external join: array(string) => string = "";
let v = join([|"a", "b"|]);
Js.log(v);
用 Join 當成範例
但是在 Javascript 中常常會有一些 function 可以任意丟入各種不同型態的參數
你可以列舉多個 function 以不同的名稱綁定
[@bs.module "Drawing"] external drawCat: unit => unit = "draw";
[@bs.module "Drawing"] external drawDog: (~giveName: string) => unit = "draw";
[@bs.module "Drawing"] external draw: (string, ~useRandomAnimal: bool) => unit = "draw";
bs.unwrap
function padLeft(value, padding) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
上面的範例中 padding
可能是數字也可能是字串
[%%raw "function padLeft(value, padding) {
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
if (typeof padding === 'string') {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}"];
[@bs.val]
external padLeft: (
string,
[@bs.unwrap] [
| `Str(string)
| `Int(int)
]
) => string = "";
let num = 4;
let str = "Message from BS: ";
padLeft("Hello world", `Int(num));
padLeft("Hello World", `Str(str));
利用 bs.unwrap
再加上 variants
的類型應用
可以得到一個參數多種輸入值得實作
關於 fs.readFileSync
的第二個參數
他可以帶入一個字串其實內含 ascii
, utf8
但是你仍然可以就像字串一樣綁定並使用
但是如果可以透過 variants
+ bs.string
來使用的話會更好
[@bs.module "fs"]
external readFileSync: (
~name: string,
[@bs.string] [
| `utf8
| [@bs.as "ascii"] `useAscii
]
) => string = "";
readFileSync(~name="xx.txt", `useAscii);
[@bs.string]
使他以同樣的名稱做編譯[@bs.as "foo"]
定義最後的名稱(alias)另外你也可以利用 [@bs.int]
將參數編譯為 int
就像 [@bs.string]
方式一樣
[@bs.val]
external test_int_type: (
[@bs.int] [
| `on_closed
| [@bs.as 20] `on_open
| `in_bin
])
=> int = "";
test_int_type(`in_bin);
note:on_closed 會編譯為 0, on_open 是 20 in_bin 則是 21
使用 readline
寫一個 Event 的範例
type readline;
[@bs.send]
external on: (
readline,
[@bs.string] [
| `close(unit => unit)
| `line(string => unit)
]
) => readline = "";
let register = rl =>
rl
-> on(`close(event => ()))
-> on(`line(line => print_endline(line)));
output
function register(rl) {
return rl.on("close", (function () {
return /* () */0;
}))
.on("line", (function (line) {
console.log(line);
return /* () */0;
}));
}
剛剛有談到 [@bs.send]
會將第一個參數當成物件
這種範例的話只能夠使用 ->
作為 Fast Pipe
|>
會造成錯誤
可以利用預定的參數給予外部的函數有時是很方便的
[@bs.val]
external process_on_exit: (
[@bs.as "exit"] _,
int => unit
) => unit = "process.on";
let () = process_on_exit(exit_code =>
Js.log("error code: " ++ string_of_int(exit_code)));
[@bs.as "exit"]
以及 _
放在一起代表你第一個參數希望放入的是字串 "exit"
也可以用 Json 關鍵字搭配
[@bs.as]
[@bs.as {json|true|json}]
- true[@bs.as {json|{"name": "John"}|json}]
- {"name": "John"}
Curry 指的是可以每次輸入較少的參數
在最後所有的參數都輸入完成後再回傳結果
let add: (int, int, int) => int;
let addFive: (int, int) => int;
let twelve: int;
add
必須丟入三個參數
addFive
則是已經取得第一個參數 還需要兩個參數
在 Javascript 這種動態語言中 currying
是危險的
忘記傳遞參數時在編譯時並不會有錯誤通知
因為上述的原因所以 函式有多個參數時
BuckleScript 很難 100% 的完整的轉譯為 Js 函式
currying
的動作,就像原本的 JS 函式一般呼叫使用throttle
debounce
promise
和 curry 邏輯相違背的則是利用一般函式來使用Js 中有很多使用會如下例
x.onload = function(v) {
console.log(this.response + v)
}
在這個範例中如果是 Javascript
的邏輯來看
this
是指向 x
他並不是沒有回傳值所以宣告 unit => unit
是錯誤的
可以利用 [@bs.this]
範例如下
type x;
[@bs.val] external x: x = "";
[@bs.set] external set_onload: (x, [@bs.this] ((x, int) => unit)) => unit = "onload";
[@bs.get] external resp: x => int ="response";
set_onload(
x,
[@bs.this] (
(o, v) => Js.log(resp(o) + v)
)
);
bs.this
和 bs
是一樣的
只是他第一個參數保留給 this
之前好像有提過 不過我忘了
就再打一次
丟入一個物件值
固定取出某個欄位得值
[%%raw "var test2 = {'a': 'aa'}"];
type x;
[@bs.val] external test2: x = "";
[@bs.get] external getA: x => string = "a";
test2 -> getA -> Js.log;